home *** CD-ROM | disk | FTP | other *** search
/ Language/OS - Multiplatform Resource Library / LANGUAGE OS.iso / gnu / dirent.lha / dirent / getdents.c < prev    next >
C/C++ Source or Header  |  1990-09-13  |  8KB  |  313 lines

  1. /*
  2.     getdents -- get directory entries in a file system independent format
  3.             (SVR3 system call emulation)
  4.  
  5.     last edit:    15-Feb-1988    D A Gwyn
  6.  
  7.     This single source file supports several different methods of
  8.     getting directory entries from the operating system.  Define
  9.     whichever one of the following describes your system:
  10.  
  11.     UFS    original UNIX filesystem (14-character name limit)
  12.     BFS    4.2BSD (also 4.3BSD) native filesystem (long names)
  13.     NFS    getdirentries() system call
  14.  
  15.     Also define any of the following that are pertinent:
  16.  
  17.     ATT_SPEC    check user buffer address for longword alignment
  18.     BSD_SYSV    BRL UNIX System V emulation environment on 4.nBSD
  19.     UNK        have _getdents() system call, but kernel may not
  20.             support it
  21.  
  22.     If your C library has a getdents() system call interface, but you
  23.     can't count on all kernels on which your application binaries may
  24.     run to support it, change the system call interface name to
  25.     _getdents() and define "UNK" to enable the system-call validity
  26.     test in this "wrapper" around _getdents().
  27.  
  28.     If your system has a getdents() system call that is guaranteed 
  29.     to always work, you shouldn't be using this source file at all.
  30. */
  31.  
  32. #ifdef unos
  33. #include    <errno.h>
  34. #else
  35. #include    <sys/errno.h>
  36. #endif
  37. #include    <sys/types.h>
  38. #ifdef BSD_SYSV
  39. #include    <sys/_dir.h>        /* BSD flavor, not System V */
  40. #else
  41. #include    <sys/dir.h>
  42. #undef    MAXNAMLEN            /* avoid conflict with SVR3 */
  43.     /* Good thing we don't need to use the DIRSIZ() macro! */
  44. #ifdef d_ino                /* 4.3BSD/NFS using d_fileno */
  45. #undef    d_ino                /* (not absolutely necessary) */
  46. #else
  47. #define    d_fileno    d_ino        /* (struct direct) member */
  48. #endif
  49. #endif
  50. #include    <sys/dirent.h>
  51. #include    <sys/stat.h>
  52. #ifdef UNK
  53. #ifndef UFS
  54. #include "***** ERROR ***** UNK applies only to UFS"
  55. /* One could do something similar for getdirentries(), but I didn't bother. */
  56. #endif
  57. #include    <signal.h>
  58. #endif
  59.  
  60. #if defined(UFS) + defined(BFS) + defined(NFS) != 1    /* sanity check */
  61. #include "***** ERROR ***** exactly one of UFS, BFS, or NFS must be defined"
  62. #endif
  63.  
  64. #ifdef BSD_SYSV
  65. struct dirent    __dirent;        /* (just for the DIRENTBASESIZ macro) */
  66. #endif
  67.  
  68. #ifdef UFS
  69. #define    RecLen( dp )    (sizeof(struct direct))    /* fixed-length entries */
  70. #else    /* BFS || NFS */
  71. #define    RecLen( dp )    ((dp)->d_reclen)    /* variable-length entries */
  72. #endif
  73.  
  74. #ifdef NFS
  75. #ifdef BSD_SYSV
  76. #define    getdirentries    _getdirentries    /* package hides this system call */
  77. #endif
  78. extern int    getdirentries();
  79. static long    dummy;            /* getdirentries() needs basep */
  80. #define    GetBlock( fd, buf, n )    getdirentries( fd, buf, (unsigned)n, &dummy )
  81. #else    /* UFS || BFS */
  82. #ifdef BSD_SYSV
  83. #define read    _read            /* avoid emulation overhead */
  84. #endif
  85. extern int    read();
  86. #define    GetBlock( fd, buf, n )    read( fd, buf, (unsigned)n )
  87. #endif
  88.  
  89. #ifdef UNK
  90. extern int    _getdents();        /* actual system call */
  91. #endif
  92.  
  93. extern char    *strncpy();
  94. extern int    fstat();
  95. extern off_t    lseek();
  96.  
  97. extern int    errno;
  98.  
  99. #ifndef DIRBLKSIZ
  100. #define    DIRBLKSIZ    4096        /* directory file read buffer size */
  101. #endif
  102.  
  103. #ifndef NULL
  104. #define    NULL    0
  105. #endif
  106.  
  107. #ifndef SEEK_CUR
  108. #define    SEEK_CUR    1
  109. #endif
  110.  
  111. #ifndef S_ISDIR                /* macro to test for directory file */
  112. #define    S_ISDIR( mode )        (((mode) & S_IFMT) == S_IFDIR)
  113. #endif
  114.  
  115. #ifdef UFS
  116.  
  117. /*
  118.     The following routine is necessary to handle DIRSIZ-long entry names.
  119.     Thanks to Richard Todd for pointing this out.
  120. */
  121.  
  122. static int
  123. NameLen( name )                /* return # chars in embedded name */
  124.     char        name[];        /* -> name embedded in struct direct */
  125. {
  126.     register char    *s;        /* -> name[.] */
  127.     register char    *stop = &name[DIRSIZ];    /* -> past end of name field */
  128.  
  129.     for ( s = &name[1];        /* (empty names are impossible) */
  130.           *s != '\0'        /* not NUL terminator */
  131.        && ++s < stop;        /* < DIRSIZ characters scanned */
  132.         )
  133.         ;
  134.  
  135.     return s - name;        /* # valid characters in name */
  136. }
  137.  
  138. #else    /* BFS || NFS */
  139.  
  140. extern int    strlen();
  141.  
  142. #define    NameLen( name )    strlen( name )    /* names are always NUL-terminated */
  143.  
  144. #endif
  145.  
  146. #ifdef UNK
  147. static enum    { maybe, no, yes }    state = maybe;
  148.                     /* does _getdents() work? */
  149.  
  150. /*ARGSUSED*/
  151. static void
  152. sig_catch( sig )
  153.     int    sig;            /* must be SIGSYS */
  154. {
  155.     state = no;            /* attempted _getdents() faulted */
  156. }
  157. #endif
  158.  
  159. int
  160. getdents( fildes, buf, nbyte )        /* returns # bytes read;
  161.                        0 on EOF, -1 on error */
  162.     int            fildes;    /* directory file descriptor */
  163.     char            *buf;    /* where to put the (struct dirent)s */
  164.     unsigned        nbyte;    /* size of buf[] */
  165. {
  166.     int            serrno;    /* entry errno */
  167.     off_t            offset;    /* initial directory file offset */
  168.     struct stat        statb;    /* fstat() info */
  169.     union    {
  170.         char        dblk[DIRBLKSIZ
  171. #ifdef UFS
  172.                      +1    /* for last entry name terminator */
  173. #endif
  174.                     ];
  175.                     /* directory file block buffer */
  176.         struct direct    dummy;    /* just for alignment */
  177.         }    u;        /* (avoids having to malloc()) */
  178.     register struct direct    *dp;    /* -> u.dblk[.] */
  179.     register struct dirent    *bp;    /* -> buf[.] */
  180.  
  181. #ifdef UNK
  182.     switch ( state )
  183.         {
  184.         void        (*shdlr)();    /* entry SIGSYS handler */
  185.         register int    retval;    /* return from _getdents() if any */
  186.  
  187.     case yes:            /* _getdents() is known to work */
  188.         return _getdents( fildes, buf, nbyte );
  189.  
  190.     case maybe:            /* first time only */
  191.         shdlr = signal( SIGSYS, sig_catch );
  192.         retval = _getdents( fildes, buf, nbyte );    /* try it */
  193.         (void)signal( SIGSYS, shdlr );
  194.  
  195.         if ( state == maybe )    /* SIGSYS did not occur */
  196.             {
  197.             state = yes;    /* so _getdents() must have worked */
  198.             return retval;
  199.             }
  200.         /* else fall through into emulation */
  201.  
  202. /*    case no:    /* fall through into emulation */
  203.         }
  204. #endif
  205.  
  206.     if ( buf == NULL
  207. #ifdef ATT_SPEC
  208.       || (unsigned long)buf % sizeof(long) != 0    /* ugh */
  209. #endif
  210.        )    {
  211.         errno = EFAULT;        /* invalid pointer */
  212.         return -1;
  213.         }
  214.  
  215.     if ( fstat( fildes, &statb ) != 0 )
  216.         return -1;        /* errno set by fstat() */
  217.  
  218.     if ( !S_ISDIR( statb.st_mode ) )
  219.         {
  220.         errno = ENOTDIR;    /* not a directory */
  221.         return -1;
  222.         }
  223.  
  224.     if ( (offset = lseek( fildes, (off_t)0, SEEK_CUR )) < 0 )
  225.         return -1;        /* errno set by lseek() */
  226.  
  227. #ifdef BFS                /* no telling what remote hosts do */
  228.     if ( (unsigned long)offset % DIRBLKSIZ != 0 )
  229.         {
  230.         errno = ENOENT;        /* file pointer probably misaligned */
  231.         return -1;
  232.         }
  233. #endif
  234.  
  235.     serrno = errno;            /* save entry errno */
  236.  
  237.     for ( bp = (struct dirent *)buf; bp == (struct dirent *)buf; )
  238.         {            /* convert next directory block */
  239.         int    size;
  240.  
  241.         do    size = GetBlock( fildes, u.dblk, DIRBLKSIZ );
  242.         while ( size == -1 && errno == EINTR );
  243.  
  244.         if ( size <= 0 )
  245.             return size;    /* EOF or error (EBADF) */
  246.  
  247.         for ( dp = (struct direct *)u.dblk;
  248.               (char *)dp < &u.dblk[size];
  249.               dp = (struct direct *)((char *)dp + RecLen( dp ))
  250.             )    {
  251. #ifndef UFS
  252.             if ( dp->d_reclen <= 0 )
  253.                 {
  254.                 errno = EIO;    /* corrupted directory */
  255.                 return -1;
  256.                 }
  257. #endif
  258.  
  259. #ifdef unos
  260.             if ( dp->d_fileno >= 0 )
  261. #else
  262.             if ( dp->d_fileno != 0 )
  263. #endif
  264.                 {    /* non-empty; copy to user buffer */
  265.                 register int    reclen =
  266.                     DIRENTSIZ( NameLen( dp->d_name ) );
  267.  
  268.                 if ( (char *)bp + reclen > &buf[nbyte] )
  269.                     {
  270.                     errno = EINVAL;
  271.                     return -1;    /* buf too small */
  272.                     }
  273.  
  274.                 bp->d_ino = dp->d_fileno;
  275.                 bp->d_off = offset + ((char *)dp - u.dblk);
  276.                 bp->d_reclen = reclen;
  277.  
  278.                 {
  279. #ifdef UFS
  280.                 /* Is the following kludge ugly?  You bet. */
  281.  
  282.                 register char    save = dp->d_name[DIRSIZ];
  283.                     /* save original data */
  284.  
  285.                 dp->d_name[DIRSIZ] = '\0';
  286.                     /* ensure NUL termination */
  287. #endif
  288.                 (void)strncpy( bp->d_name, dp->d_name,
  289.                            reclen - DIRENTBASESIZ
  290.                          );    /* adds NUL padding */
  291. #ifdef UFS
  292.                 dp->d_name[DIRSIZ] = save;
  293.                     /* restore original data */
  294. #endif
  295.                 }
  296.  
  297.                 bp = (struct dirent *)((char *)bp + reclen);
  298.                 }
  299.             }
  300.  
  301. #if !(defined(BFS) || defined(sun))    /* 4.2BSD screwed up; fixed in 4.3BSD */
  302.         if ( (char *)dp > &u.dblk[size] )
  303.             {
  304.             errno = EIO;    /* corrupted directory */
  305.             return -1;
  306.             }
  307. #endif
  308.         }
  309.  
  310.     errno = serrno;            /* restore entry errno */
  311.     return (char *)bp - buf;    /* return # bytes read */
  312. }
  313.